﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

using alib;
using alib.Debugging;
using alib.Enumerable;

namespace agree
{
	public abstract partial class ChartControl<T, W> : ChartResources<W>
		where T : ChartControl<T, W>
		where W : struct, IProximalContext<W>
	{
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Active chart edge: binds a sequence interlock to a passive edge notification closure
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		public unsafe sealed class ActiveEdge : identity_seq, IAtomicSequence
		{
			public ActiveEdge(bool f_rightwards, ChartControl<T, W> ctrl, SyntaxRule rule, ArrayTfs tfs, IReadOnlyList<IChartObj> _dtrs, W ctx)
				: base(ctrl)
			{
				Debug.Assert(!tfs.ArgsRestricted);

				this.f_rightwards = f_rightwards;
				this.ctrl = ctrl;
				this.rule = rule;
				this.dtrs = _dtrs;
				this.c_partial = dtrs.Count;
				Debug.Assert(c_partial > 0);
				this.ctx = ctx;

				/// number of rule daughters
				int ix_last = rule.DaughterArity - 1;

				/// it's the last ARG if the number of completed daughters so far is one less than the number of rule ARGs
				this.f_last = c_partial == ix_last;

				/// index of the active daughter
				this.i_arg = (byte)(f_rightwards ? c_partial : ix_last - c_partial);

				var rgls = tfs[tfs.im.RuleArgsPath].ListSlots;
				if (rgls.Length != rule.DaughterArity)
					throw new Exception("Incorrect daughter count in active chart TFS.");	// perhaps because ARGS pruning deleted them?

				this.partial_dtr = rgls[i_arg];

				if (!partial_dtr.IsValid)
					throw new Exception("Partial daughter not found for active chart TFS.");

				this.ArityLimit = int.MaxValue;

				Interlocked.Increment(ref ctrl.c_act);
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			///
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

			public ChartControl<T, W> ctrl;
			public readonly SyntaxRule rule;
			public readonly IReadOnlyList<IChartObj> dtrs;
			public W ctx;

			TfsSlot partial_dtr;
			public readonly int c_partial;
			public int ArityLimit;
			readonly byte i_arg;
			public readonly bool f_rightwards;
			readonly bool f_last;

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// If any daughter incorporated into the active edge (with binary rules there's only one) becomes packed,
			/// return 'false' to signal that the active edge should immediately cease all activity. It does this by
			/// setting itself to the 'remove' state, which will later cause opportunistic removal of the active edge
			/// from the notification list to which it is subscribed.
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			bool check_status()
			{
				IReadOnlyList<IChartObj> _dtrs;
				if ((_dtrs = dtrs) == null)
					return false;

				IChartObj co;
				for (int i = 0; i < _dtrs.Count; i++)
					if ((co = _dtrs[i]) != null && co.IsHoldOrRemove())
					{
						if (this.TryRemove())
							this.partial_dtr = default(TfsSlot);
						return false;
					}
				return true;
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			/// <summary>
			/// Active edge: a partial chart edge. The active edge tries to match the given PassiveChart edge.
			/// Three cases are possible:
			/// 1.) the specified edge does not extend this active edge; or
			/// 2.) the rule is complete, create a new passive edge and insert it into the chart; or
			/// 3.) the rule is still not complete: create a new active edge which incorporates the new partial result.
			/// In any of these three cases, this rule continues to accept future edges as before.
			/// </summary>
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public void TryMatchEdge(IChartObj<W> candidate)
			{
				W ctx_new;
				W ctx_cnd = candidate.ProximalContext;
				if (!ctx.CombineWith(ctx_cnd, out ctx_new))
					return;

				if (ctrl is ParseControl && f_last && rule.IsSpanOnly && ctx_new.CoverageCount < ctrl.spanning_count)
					return;

				Rule d_rule = candidate.ChartLicense as Rule;
				if (d_rule != null && !rule.CheckDaughterCompatibility(d_rule, i_arg))
				{
					//chart.ctrl.c_stats(ip => ((Stats._chart*)ip)->RuleFiltered());
					return;
				}

				if (candidate.IsHoldOrRemove() || !check_status())
					return;

				if (ctrl.qc != null && ctrl.qc._QuickCheck(candidate.Tfs, partial_dtr.tfs, partial_dtr.out_mark))
					return;

				Tfs.Flags opts = ctrl.unify_opts;
				if (f_last)
					opts |= Tfs.Flags.DeleteArgs;

				if (!check_status())
					return;

				ArrayTfs new_mother = ctrl.u.UnifySection(partial_dtr, candidate, opts);
				Interlocked.Increment(ref ctrl.c_unif);
				if (new_mother == null)
					return;
				new_mother._set_trace(ctrl);

				var _dtrs = dtrs;
				if (!check_status())
					return;

				int i;
				var npo = new IChartObj[(i = c_partial) + 1];
				npo[i] = npo[0] = candidate;
				do
					npo[f_rightwards ? --i : i--] = dtrs[i];
				while (i > 0);

				if (f_last)
					ctrl.ProcessCompletedRule(rule, new_mother, npo, ctx_new);
				else
					ctrl.AddActiveEdge(new ActiveEdge(f_rightwards, ctrl, rule, new_mother, npo, ctx_new));
			}

			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			///
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			public override string ToString()
			{
				return String.Format("{0:X6}\r\n{1}", this.Id, rule.Name);
			}

			///////////////////////////////////////////////////////////////////////
			/// 
			[DebuggerBrowsable(DebuggerBrowsableState.Never)]
			internal ActiveEdge m_next;	/// linked list managed by 'NotificationSource'
			/// 
			///////////////////////////////////////////////////////////////////////
		};
	};
}
